package com.mec.taskassign.algorithm;


import com.mec.taskassign.model.Place;
import com.mec.taskassign.utils.CommonUtils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;

//@Component
public class OpsRoute {

    private ArrayList<Place> list = new ArrayList<Place>();
    private ArrayList<Place> taskSeq = new ArrayList<Place>();
    private Place v0;
    private Place[] Place;
    private Place[] middlePlace;
    private float maxLastTime;
    private float[][] distance;
    private float carSpeed;

    //构造器  输入 1.起始节点，2.所有节点信息：时间窗、收益、名称， 3.中途必须经过的节点：时间窗，收益，名称， 4. 所有节点两两之间距离矩阵; 5. 车速
    public OpsRoute(Place v0, Place[] Place, Place[] middlePlace, float[][] distance, float carSpeed) {
        this.v0 = v0;
        this.Place = Place;
        if (middlePlace.length > 0) {
            this.middlePlace = transferMiddlePosition(v0, middlePlace);
        } else {
            this.middlePlace = middlePlace;
        }

        this.distance = distance;
        this.carSpeed = carSpeed;
    }

    public OpsRoute() {

    }

    private Place[] transferMiddlePosition(Place vx, Place[] middlePosition) {
        boolean check = false;
        for (int i = 0; i < middlePosition.length; i++) {
            if (vx.getId() == middlePosition[i].getId()) {
                check = true;
            }
        }
        if (check) {
            Place[] updateMiddle = new Place[middlePosition.length - 1];
            int count = 0;
            for (int i = 0; i < middlePosition.length; i++) {
                if (vx.getId() != middlePosition[i].getId()) {
                    updateMiddle[count] = middlePosition[i];
                    count++;
                }
            }
            return updateMiddle;

        } else {
            return middlePosition;
        }

    }


    //找到输入节点序列中收益最大的
    private Place findMaxPay(Place[] Place) {
        Place check = null;
        if (Place == null) {
            return check;
        } else {
            check = Place[0];
            for (int i = 0; i < Place.length; i++) {

                if (Place[i].getEarn() > check.getEarn()) {
                    check = Place[i];
                }
            }
        }

        return check;
    }

    //仅在程序开始时使用，根据时间窗和距离确认途中必经节点执行顺序，并更新时间窗
    private Place[] findShortestNode(Place start, Place[] Place) {

        for (int i = 0; i < Place.length - 1; i++) {   //根据距离确认必经节点执行顺序
            for (int j = i + 1; j < Place.length; j++) {
                if (distance[start.getId()][Place[i].getId()]
                        > distance[start.getId()][Place[j].getId()]) {
                    Place temp = Place[i];
                    Place[i] = Place[j];
                    Place[j] = temp;
                }
            }
        }

        int max = Place.length - 1;
        //System.out.println(max);
        Place[max].setTime(Place[max].getTime() - distance[start.getId()][Place[max].getId()] / carSpeed);
        float total = distance[start.getId()][Place[max].getId()] / carSpeed;
        for (int i = max - 1; i >= 0; i--) {
            total = total + distance[Place[i + 1].getId()][Place[i].getId()] / carSpeed;
            Place[i].setTime(Place[i].getTime() - total);//更新时间窗
        }

        return Place;
    }

    //在必经子路径中确认最终执行时间
    public float findLastTime(Place[] Place) {
        float lastTime = Place[0].getTime();
        for (int i = 0; i < Place.length; i++) {
            if (Place[i].getTime() > lastTime) {
                lastTime = Place[i].getTime();
            }
        }
        return lastTime;
    }

    //给定子路径中的开始和结束节点，并从剩下的所有节点中插入一个可以执行的节点
    public Place findOneInsertNode(Place start, Place end, ArrayList<Place> Place) {
        Place check = null;
        Place[] possible = new Place[100];
        int index = 0;
        for (int i = 0; i < Place.size(); i++) {
            if ((start.getTime() + distance[Place.get(i).getId()][end.getId()] / carSpeed +
                    distance[start.getId()][Place.get(i).getId()] / carSpeed) <= end.getTime()) {
                possible[index] = Place.get(i);
                index++;
            }
        }
        Place[] strongPossible = new Place[index];
        for (int i = 0; i < strongPossible.length; i++) {
            strongPossible[i] = possible[i];
        }

        if (strongPossible.length == 0) {
            strongPossible = null;
        }
        check = findMaxPay(strongPossible);
        return check;
    }

    //根据节点名称获取节点在list中位值
    public int getIndex(int nodeName, ArrayList<Place> list) {
        int check = 0;
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).getId() == nodeName) {
                check = i;
            }
        }
        return check;
    }

    public void print(Place[] Place) {

        for (int i = 0; i < Place.length; i++) {
            System.out.print(Place[i].getId() + " " + Place[i].getJing());
        }

        System.out.println();
    }

    public void printList(ArrayList<Place> list) {
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i).getId() + ":" + list.get(i).getTime() + " ");
        }
        System.out.println();
    }

    //整个算法的开始，参数（初始节点v0，所有节点序列，途中必经节点序列（确认子路径），所有节点两两之间距离）
    public ArrayList<Place> startOps() {
        //将所有任务添加至list中（此list不是最终的任务执行list）
        for (int i = 0; i < Place.length; i++) {
            list.add(Place[i]);
        }

        //存在必经节点时
        if (middlePlace.length > 0) {
            maxLastTime = findLastTime(middlePlace);
            //找到中间必经节点的顺序并把这些顺序添加到taskSeq中，taskSeq为最终执行顺序
            Place[] newMiddlePlace = findShortestNode(v0, middlePlace);
            //Place v00=new Place(middlePlace[middlePlace.length-1].getTime(), v0.getEarn(), 0);System.out.println(v00.getTime());

            //v00本质就是v0，此处是区分v0作为终点和起点
            //Place v00=new Place(maxLastTime, v0.getEarn(), v0.getid(),v0.getJing(),v0.getWei(),v0.getPri());
            Place v00 = new Place();
            v00.setTime(maxLastTime);
            v00.setEarn(v0.getEarn());
            v00.setId(v0.getId());
            v00.setJing(v0.getJing());
            v00.setWei(v0.getWei());
            v00.setPri(v0.getPri());

            ////////////////将起点从list中移除
            v0.setTime(0);
            list.remove(getIndex(v0.getId(), list));
            ////////////////
            //askSeq.add(v0);
            taskSeq.add(v0);

            for (int i = 0; i < newMiddlePlace.length; i++) {
                taskSeq.add(newMiddlePlace[i]);
            }

            //将taskSeq中出现的点从list中去除
            for (int i = 0; i < newMiddlePlace.length; i++) {
                for (int j = 0; j < list.size(); j++) {
                    if (newMiddlePlace[i].getId() == list.get(j).getId()) {
                        list.remove(j);
                    }
                }
            }


            //开始在各个子序列中插入节点
            Place start = v0;
            Place oneNode = null;
            Place end = newMiddlePlace[0];
            float total = 0;

            //对于每一个子序列遍历
            for (int i = 0; i < newMiddlePlace.length; i++) {
                if ((oneNode = findOneInsertNode(start, end, list)) == null) {
                    ;
                } else {
                    //对于每一个子序列（除去最后一个），当发现每一个子序列中可以继续插入节点时，就循环执行，并在每一次执行后修改时间窗信息。
                    //每一次执行后都会在taskSeq插入节点，并在list中删除插入的节点
                    while ((oneNode = findOneInsertNode(start, end, list)) != null) {
                        oneNode.setTime(end.getTime() - distance[oneNode.getId()][end.getId()] / carSpeed);
                        list.remove(getIndex(oneNode.getId(), list));
                        taskSeq.add(getIndex(end.getId(), taskSeq), oneNode);
                        end = oneNode;
                    }
                    end = newMiddlePlace[i];
                    total = start.getTime();
                    for (int k = getIndex(start.getId(), taskSeq); k < taskSeq.size(); k++) {

                        if (taskSeq.get(k).getId() == end.getId()) {
                            break;
                        }
                        total = total + distance[taskSeq.get(k).getId()][taskSeq.get(k + 1).getId()] / carSpeed;
                    }
                    taskSeq.get(getIndex(end.getId(), taskSeq)).setTime(total);
                }

                if (i != newMiddlePlace.length - 1) {
                    start = newMiddlePlace[i];
                    start.setTime(taskSeq.get(getIndex(start.getId(), taskSeq)).getTime());
                    end = newMiddlePlace[i + 1];
                    end.setTime(taskSeq.get(getIndex(end.getId(), taskSeq)).getTime());
                } else {
                    start = newMiddlePlace[i];
                    start.setTime(taskSeq.get(getIndex(start.getId(), taskSeq)).getTime());
                    end = v00;


                }

            }

            //对于最后一个子序列（这个子序列末节点是v0，也就是回到了原先的位置），寻找可以插入的点
            Place check = findOneInsertNode(start, end, list);
            if (check == null) {
                taskSeq.add(v00);
                System.out.println("no position in the last sub route!");
            } else {
                while ((oneNode = findOneInsertNode(start, end, list)) != null) {
                    oneNode.setTime(end.getTime() - distance[oneNode.getId()][end.getId()] / carSpeed);
                    list.remove(getIndex(oneNode.getId(), list));
                    taskSeq.add(getIndex(start.getId(), taskSeq) + 1, oneNode);
                    end = oneNode;
                }
                end = v00;
                total = start.getTime();
                taskSeq.add(v00);
                //printList(taskSeq);
                for (int k = getIndex(start.getId(), taskSeq); k < taskSeq.size(); k++) {

                    if (taskSeq.get(k).getId() == end.getId()) {
                        break;
                    }
                    total = total + distance[taskSeq.get(k).getId()][taskSeq.get(k + 1).getId()] / carSpeed;
                }
                taskSeq.get(taskSeq.size() - 1).setTime(total);
            }
            printList(taskSeq);      //输出taskSequence中所有节点名字和相应更新时间窗口    【0:0 3:9 1:12 2:12 5:18 7:26 4:30 0:40 】
        }

        //没有必经节点时
        else if (middlePlace.length == 0) {
            maxLastTime = v0.getTime();
            Place v00 = new Place();
            v00.setTime(maxLastTime);
            v00.setEarn(v0.getEarn());
            v00.setId(v0.getId());
            v00.setJing(v0.getJing());
            v00.setWei(v0.getWei());
            v00.setPri(v0.getPri());
            ////////////////将起点从list中移除
            v0.setTime(0);
            list.remove(getIndex(v0.getId(), list));
            ////////////////
            taskSeq.add(v0);

            //开始插入节点
            Place start = v0;
            Place oneNode = null;
            Place end = v00;
            float total = 0;
            Place check = findOneInsertNode(start, end, list);
            if (check == null) {
                System.out.println("only one position in route!");
            } else {
                while ((oneNode = findOneInsertNode(start, end, list)) != null) {
                    oneNode.setTime(end.getTime() - distance[oneNode.getId()][end.getId()] / carSpeed);
                    list.remove(getIndex(oneNode.getId(), list));
                    taskSeq.add(getIndex(start.getId(), taskSeq) + 1, oneNode);
                    end = oneNode;
                }
                end = v00;
                total = start.getTime();
                taskSeq.add(v00);
                //printList(taskSeq);
//                for(int k=getIndex(start.getId(),taskSeq);k<taskSeq.size();k++){
//
//                    if(taskSeq.get(k).getId()==end.getId()){
//                        break;
//                    }
//                    total=total+distance[taskSeq.get(k).getId()][taskSeq.get(k+1).getId()];
//                }
                for (int k = 0; k < taskSeq.size() - 1; k++) {
                    total = total + distance[taskSeq.get(k).getId()][taskSeq.get(k + 1).getId()] / carSpeed;
                }
                taskSeq.get(taskSeq.size() - 1).setTime(total);
            }
            printList(taskSeq);      //输出taskSequence中所有节点名字和相应更新时间窗口    【0:0 3:9 1:12 2:12 5:18 7:26 4:30 0:40 】
        }

//        String output="";
//        for(int i=0;i<taskSeq.size();i++){
//
//            output=output+"v"+taskSeq.get(i).getid()+"  ";
//
//        }
        return taskSeq;
    }
}
